#include <UnitTest++.h>
#include <cstdio>
#include <iostream>
#include <fstream>
#include <string>
#include "compile.h"
#include "preproc.h"
#include "vcc.h"
#include "VccException.h"
#include "memory.h"
#include <stdlib.h>
#include <sstream>
#include <boost/format.hpp>
#include "testing.h"
#include "FileTooBigException.h"
#include "UnableToOpenFileException.h"

std::string file_to_compile;

bool output_mode_quiet;
bool output_mode_verbose;

// input/output file handles.
FILE* inf;
FILE* outf;

// compiling events/effects/misc scripts
char effect = 0;
char scrpt = 0;

char magic = 0;

// ptr -> source file in memory.
char* source;
char chr_tablebeta[256];
char tokenbeta[512];

const int MAX_BUFFER_SIZE = 100000;

char* LoadTextFile(std::string source_file, const int max_buffer_size) {
    std::ifstream input(source_file.c_str(), std::ios::in | std::ios::binary | std::ios::ate);
    if (!input.is_open()) {
        throw UnableToOpenFileException(source_file);
    }
    long file_size = input.tellg();
    if (file_size > max_buffer_size) {
        throw FileTooBigException(source_file, file_size, max_buffer_size);
    }
    char* data = NULL; 
    AllocateCharArray(data, file_size + sizeof('\0'));
    
    input.seekg(0, std::ios::beg);
    input.read(data, file_size);
    input.close();
    
    data[file_size] = '\0';
    return data;
}

const std::string TEMPORARY_FILE = "$$tmep$$.ma_";

void PostStartupFiles() {
    source = LoadTextFile(TEMPORARY_FILE);
}

void WriteMagicOutput()
{ FILE *f;

         f=fopen("MAGIC.VCS","wb");
         fwrite (&numscripts, 1, 4, f);
         fwrite (scriptofstbl, 4, numscripts, f);
         fwrite (code, 1, (cpos-code), f);
         fclose (f);

         remove("ERROR.TXT");
         remove("$$TMEP$$.MA_");
}

void WriteEffectOutput()
{ FILE *f;

         f=fopen("EFFECTS.VCS","wb");
         fwrite (&numscripts, 1, 4, f);
         fwrite (scriptofstbl, 4, numscripts, f);
         fwrite (code, 1, (cpos-code), f);
         fclose (f);

         remove("ERROR.TXT");
         remove("$$TMEP$$.MA_");
}

void WriteScriptOutput()
{ FILE *f;

         f=fopen("STARTUP.VCS","wb");
         fwrite (&numscripts, 1, 4, f);
         fwrite (scriptofstbl, 4, numscripts, f);
         fwrite (code, 1, (cpos-code), f);
         fclose (f);

         remove("ERROR.TXT");
         remove("$$TMEP$$.MA_");
}

void WriteOutput() {
    FILE* f;
    char i;
    short mx;
    short my;
    int a;

    std::string map_filename = file_to_compile + ".map";

    f = fopen(map_filename.c_str(), "rb+");
    if (!f) {
        if (!output_mode_quiet) {
            std::cout << "*error* Could not open map file." << std::endl;
        }
        exit(-1);
    }
    
    fseek(f, 68, 0);
    fread(&mx, 1, 2, f);
    fread(&my, 1, 2, f);
    fseek(f, 100 + (mx * my * 5) + 7956, 0);
    fread(&a, 1, 4, f);
    fseek(f, 88*a, 1);
    fread(&i, 1, 1, f);
    fread(&a, 1, 4, f);

    fseek(f, (i * 4) + a, 1);
    fwrite(&numscripts, 1, 4, f);
    fwrite(scriptofstbl, 4, numscripts, f);
    fwrite(code, 1, cpos - code, f);
    fclose(f);
    
    remove("ERROR.TXT");
    remove("$$TMEP$$.MA_");
}

void DisplayUsage() {
    std::cout
        << "vcc v.05.May.98 Copyright (C)1997 BJ Eirich" << std::endl
        << "Usage: vcc <vc file> [flag]" << std::endl
        << "Where [flag] is one of the following:" << std::endl
        << "        q   Quiet mode - no output." << std::endl
        << "        v   Super-Verbose mode - more output than you want." << std::endl;
}

void ParseCommandLineArguments(int argc, char* argv[]) {
    if (argc == 2 && !strcmp(argv[1], "test")) {
        try {
            SetTesting(true);
            UnitTest::RunAllTests();
        } catch (VccException exc) {
            std::cout << "caught vcc exception: " << exc.get_message() << std::endl;
        } catch (...) {
            std::cout << "caught exception in RunAllTests" << std::endl;
        }
        exit(0);
    }
    
    switch (argc) {
        case 1: {
            DisplayUsage();
            exit(-1);
        }
        case 2: {
            file_to_compile = argv[1];
            if (!stricmp(file_to_compile.c_str(), "EFFECTS")) {
                effect = 1;
            }
            else if (!stricmp(file_to_compile.c_str(), "MAGIC")) {
                magic = 1;
            }
            else if (!stricmp(file_to_compile.c_str(), "STARTUP")) {
                scrpt = 1;
            }
            break;
        }
        case 3: {
            char* arg = argv[2];
            if (arg[0] == 'q') {
                output_mode_quiet = true;
            }
            else if (arg[0] == 'v') {
                output_mode_verbose = 1;
            }
        }
        default: {
            std::cout << "vcc: Too many parameters." << std::endl;
            exit(-1);
        }
    }
}

namespace VERGE1 {

    void DisplayCopyright() {
        std::cout << "vcc v.05.May.98 Copyright (C)1997 BJ Eirich" << std::endl;
    }
}

void RunVccCompiler() {
    if (!output_mode_quiet) {
        VERGE1::DisplayCopyright();
    }
    
    std::string strScriptFilename = file_to_compile + ".vc";
    source = LoadTextFile(strScriptFilename);
    
    Compile();
    
    if (effect) {
        WriteEffectOutput();
    }
    else if (magic) {
        WriteMagicOutput();
    }
    else if (scrpt) {
        WriteScriptOutput();
    }
    else {
        WriteOutput();
    }
}

int main(int argc, char* argv[]) {
    try {
        ParseCommandLineArguments(argc, argv);
        RunVccCompiler();
    } catch (VccException exc) {
        std::cout << "*exception* " << exc.get_message() << std::endl;
    } catch (...) {
        std::cout << "caught an exception" << std::endl;
    }
}
